package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.config.VodConstantProperties;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.UploadFileUtil;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumTrackListVo;
import com.atguigu.tingshu.vo.album.TrackInfoVo;
import com.atguigu.tingshu.vo.album.TrackListVo;
import com.atguigu.tingshu.vo.album.TrackMediaInfoVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qcloud.vod.VodUploadClient;
import com.qcloud.vod.model.VodUploadRequest;
import com.qcloud.vod.model.VodUploadResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.sound.midi.Track;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

    @Autowired
    private TrackInfoMapper trackInfoMapper;

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private TrackStatMapper trackStatMapper;

    @Autowired
    private VodConstantProperties vodConstantProperties;

    @Autowired
    private VodService vodService;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Override
    public List<TrackInfo> findPaidTrackInfoList(Long trackId, Integer trackCount) {
        //  1.  根据声音Id查询声音对象：select * from track_info where id = 48241;
        TrackInfo trackInfo = this.getById(trackId);
        //  2.  查询声音列表： select * from track_info where order_num > 6 and album_id = 1429 and id not in (7,8) limit 10 ;
        //  2.1 查询用户已购买的声音列表.
        Result<List<Long>> userPaidTrackListResult = userInfoFeignClient.findUserPaidTrackList(trackInfo.getAlbumId());
        Assert.notNull(userPaidTrackListResult,"获取已购买声音返回结果集为空");
        List<Long> trackIdList = userPaidTrackListResult.getData();
        //  声明一个集合来存储声音列表.
        List<TrackInfo> trackInfoList = new ArrayList<>();
        //  判断购买集数：
        if (trackCount>0){
            //  2.2 构建查询条件
            LambdaQueryWrapper<TrackInfo> wrapper = new LambdaQueryWrapper<>();
            wrapper.gt(TrackInfo::getOrderNum,trackInfo.getOrderNum());
            wrapper.eq(TrackInfo::getAlbumId,trackInfo.getAlbumId());
            //  判断这个用户是否购买过声音列表.
            if (!CollectionUtils.isEmpty(trackIdList)){
                wrapper.notIn(TrackInfo::getId,trackIdList);
            }
            wrapper.last("limit "+ trackCount);
            //  返回集合
           trackInfoList = trackInfoMapper.selectList(wrapper);
        } else {
            //  购买本集
            trackInfoList.add(trackInfo);
        }
        //  返回声音集合列表
        return trackInfoList;
    }

    @Override
    public List<Map> findUserTrackPaidList(Long trackId) {
        //  先根据声音Id 查询到声音对象 select order_num from track_info where id = 48241;
        TrackInfo trackInfo = this.getById(trackId);
        //  获取当前专辑Id对应的声音列表： 已购买过的！
        Result<List<Long>> trackIdListResult = userInfoFeignClient.findUserPaidTrackList(trackInfo.getAlbumId());
        //  判断
        Assert.notNull(trackIdListResult, "声音Id集合列表为空");
        List<Long> trackIdList = trackIdListResult.getData();
        Assert.notNull(trackIdList, "声音Id集合为空");
        //  获取到当前专辑{albumId}中大于{orderNum}当前声音Id的集合列表
        //  自定义查询条件 select order_num from track_info where id = 48241; select * from track_info where order_num > 6 and album_id = ?;
        LambdaQueryWrapper<TrackInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TrackInfo::getAlbumId, trackInfo.getAlbumId()).gt(TrackInfo::getOrderNum, trackInfo.getOrderNum());
        //  大于当前OrderNum 声音的集合列表.
        List<TrackInfo> trackInfoList = trackInfoMapper.selectList(wrapper);
        //  排除已购买的声音Id 列表！ 找到哪些是没有购买的声音Id列表.
        List<Long> trackIdNoReaptList = new ArrayList<>();
        //  获取到所有声音Id 集合列表
        List<Long> trackIdAllList = trackInfoList.stream().map(TrackInfo::getId).collect(Collectors.toList());
        //  判断 用没有购买过声音Id
        if (CollectionUtils.isEmpty(trackIdList)) {
            //   获取需要购买的声音Id列表
            trackIdNoReaptList = trackIdAllList;
        } else {
            //   用购买过声音Id 获取需要购买的声音Id列表
            trackIdNoReaptList = trackIdAllList.stream().filter(trackIds -> !trackIdList.contains(trackIds)).collect(Collectors.toList());
        }
        //  根据专辑Id 获取到专辑对象
        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());

        //  声明集合来存储map
        List<Map> mapList = new ArrayList<>();
        //  根据剩余集数构建购买声音集合列表.
        if (trackIdNoReaptList.size() >= 0) {
            //  声明map 集合
            Map<String, Object> map = new HashMap<>();
            map.put("name", "本集"); // 显示文本
            map.put("price", albumInfo.getPrice()); // 专辑声音对应的价格
            map.put("trackCount", 0); // 记录购买集数
            //  添加到集合中
            mapList.add(map);
        }
        //  处理后10集   trackIdNoReaptList == 9;  trackIdNoReaptList == 19;
        if (trackIdNoReaptList.size() > 0 && trackIdNoReaptList.size() <= 10) {
            //  声明map 集合
            Map<String, Object> map = new HashMap<>();
            //  获取到当前集合长度
            int count = trackIdNoReaptList.size();
            map.put("name", "后"+count+"集"); // 显示文本 new BigDecimal("字符串") 能够防止精度损失.
            map.put("price", albumInfo.getPrice().multiply(new BigDecimal(String.valueOf(count)))); // 专辑声音对应的价格
            map.put("trackCount", count); // 记录购买集数
            //  添加到集合中
            mapList.add(map);
        }
        //   trackIdNoReaptList == 19; 本集
        if (trackIdNoReaptList.size()>10){
            //  声明map 集合
            Map<String, Object> map = new HashMap<>();
            //  获取到当前集合长度
            map.put("name", "后10集"); // 显示文本
            map.put("price", albumInfo.getPrice().multiply(new BigDecimal("10"))); // 专辑声音对应的价格
            map.put("trackCount", 10); // 记录购买集数
            //  添加到集合中
            mapList.add(map);
        }
        // 后20集
        if(trackIdNoReaptList.size() > 10 && trackIdNoReaptList.size() <= 20) {
            Map<String, Object> map = new HashMap<>();
            int count = trackIdNoReaptList.size();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(String.valueOf(count)));
            map.put("name", "后"+count+"集");
            map.put("price", price);
            map.put("trackCount", count);
            mapList.add(map);
        }
        if(trackIdNoReaptList.size() > 20) {
            Map<String, Object> map = new HashMap<>();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal("20"));
            map.put("name", "后20集");
            map.put("price", price);
            map.put("trackCount", 20);
            mapList.add(map);
        }

        //后30集
        if(trackIdNoReaptList.size() > 20 && trackIdNoReaptList.size() <= 30) {
            Map<String, Object> map = new HashMap<>();
            int count = trackIdNoReaptList.size();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(String.valueOf(count)));
            map.put("name", "后"+count+"集");
            map.put("price", price);
            map.put("trackCount", count);
            mapList.add(map);
        }
        if(trackIdNoReaptList.size() > 30) {
            Map<String, Object> map = new HashMap<>();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal("30"));
            map.put("name", "后30集");
            map.put("price", price);
            map.put("trackCount", 30);
            mapList.add(map);
        }

        //后50集
        if(trackIdNoReaptList.size() > 30 && trackIdNoReaptList.size() <= 50) {
            Map<String, Object> map = new HashMap<>();
            int count = trackIdNoReaptList.size();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal(String.valueOf(count)));
            map.put("name", "后"+count+"集");
            map.put("price", price);
            map.put("trackCount", count);
            mapList.add(map);
        }
        // 最多购买50集;
        if(trackIdNoReaptList.size() > 50) {
            Map<String, Object> map = new HashMap<>();
            BigDecimal price = albumInfo.getPrice().multiply(new BigDecimal("50"));
            map.put("name", "后50集");
            map.put("price", price);
            map.put("trackCount", 50);
            mapList.add(map);
        }
        //  返回集合列表
        return mapList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStat(Long albumId, Long trackId, String statType, Integer count) {
        //  更新两个地方. track_stat; album_stat
        trackInfoMapper.updateTrackStat(trackId, statType, count);
        //  更新专辑：
        if (statType.equals(SystemConstant.TRACK_STAT_PLAY)) {
            //  调用方法.
            albumInfoMapper.updateAlbumStat(albumId, SystemConstant.ALBUM_STAT_PLAY, count);
        }
    }

    @Override
    public IPage<AlbumTrackListVo> findAlbumTrackPage(Page<AlbumTrackListVo> albumTrackListVoPage, Long albumId, Long userId) {
        //	1. 根据专辑Id找到声音列表!
        IPage<AlbumTrackListVo> pageInfo = trackInfoMapper.selectAlbumTrackPage(albumTrackListVoPage, albumId);
        //	看专辑的付费类型.
        AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
        //	2. 判断用户Id 是否为空
        if (null == userId) {
            //	先过滤掉免费的试听集数
            List<AlbumTrackListVo> albumTrackListVoList = pageInfo.getRecords().stream().filter(albumTrackListVo -> albumTrackListVo.getOrderNum() > albumInfo.getTracksForFree()).collect(Collectors.toList());
            //	判断专辑只要是付费{付费 或 vip免费}
            if (!SystemConstant.ALBUM_PAY_TYPE_FREE.equals(albumInfo.getPayType())) {
                //	循环设置付费标识
                for (AlbumTrackListVo albumTrackListVo : albumTrackListVoList) {
                    //	表示付费
                    albumTrackListVo.setIsShowPaidMark(true);
                }
            }
        } else {
            //	声明一个字段来标识是否需要付费.
            boolean isNeedPaid = false;
            //	专辑类型 0102-vip免费
            if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(albumInfo.getPayType())) {
                //	根据用户Id 来获取到用户对象
                Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfoVo(userId);
                Assert.notNull(userInfoVoResult, "返回用户结果集为空");
                UserInfoVo userInfoVo = userInfoVoResult.getData();
                Assert.notNull(userInfoVo, "返回用户为空");
                //	判断用户的身份 这个用户不是vip
                if (userInfoVo.getIsVip() == 0) {
                    //  表示需要付费
                    isNeedPaid = true;
                }
                //  这个用户是vip ，但是这个用户已经过期了!
                if (userInfoVo.getIsVip() == 1 && userInfoVo.getVipExpireTime().before(new Date())) {
                    //  表示需要付费
                    isNeedPaid = true;
                }
            } else if (SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(albumInfo.getPayType())) {
                //  表示需要付费
                isNeedPaid = true;
            }
            //	统一处理是否需要付费.
            if (isNeedPaid) {
                //  获取专辑Id对应的声音列表{除去试听集数}
                List<AlbumTrackListVo> albumTrackNeedPaidListVoList = pageInfo.getRecords().stream().filter(albumTrackListVo -> albumTrackListVo.getOrderNum() > albumInfo.getTracksForFree()).collect(Collectors.toList());
                //  获取到当前需要付费的声音Id 列表.
                List<Long> trackIdList = albumTrackNeedPaidListVoList.stream().map(AlbumTrackListVo::getTrackId).collect(Collectors.toList());
                //  需要排除掉用户已购买的声音Id! 查询当前这个用户购买的声音有哪些?
                //  trackId 1;买过  trackId 0 未购买
                Result<Map<Long, Integer>> mapResult = userInfoFeignClient.userIsPaidTrack(albumId, trackIdList);
                //  判断
                Assert.notNull(mapResult, "返回用户购买声音结果集为空");
                Map<Long, Integer> map = mapResult.getData();
                Assert.notNull(map, "返回用户购买声音集合为空");
                //  处理是否需要付费标识：
                //  setIsShowPaidMark=true: 显示付费标识； false: 不需要显示付费标识：
                //  循环遍历需要付费的集合列表.
                for (AlbumTrackListVo albumTrackListVo : albumTrackNeedPaidListVoList) {
                    //  判断是否需要显示付费标识
                    Boolean isBuy = map.get(albumTrackListVo.getTrackId()) == 0 ? true : false;
                    //  Boolean isBuy = map.get(albumTrackListVo.getTrackId()) == 1 ? false : true;
                    albumTrackListVo.setIsShowPaidMark(isBuy);
                }
            }
        }
        //  返回数据
        return pageInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTrackInfo(Long trackId, TrackInfoVo trackInfoVo) {
        //	思考：1. 云点播 2.track_info
        //	如何知道当前声音被修改了? 通过流媒体声音Id{media_file_id}
        //	原来的流媒体声音Id
        TrackInfo trackInfo = this.getById(trackId);
        String mediaFileId = trackInfo.getMediaFileId();
        //	进行属性拷贝：需要将页面修改的非声音数据，赋值给trackInfo对象.
        BeanUtils.copyProperties(trackInfoVo, trackInfo);
        //	判断原来的流媒体声音Id 与 现在的流媒体声音Id 是否一致！
        if (!mediaFileId.equals(trackInfoVo.getMediaFileId())) {
            //	说明当前声音已被修改过. 变动流媒体对应的字段.
            TrackMediaInfoVo trackMediaInfo = vodService.getTrackMediaInfo(trackInfoVo.getMediaFileId());
            //	判断
            if (null == trackMediaInfo) {
                throw new GuiguException(ResultCodeEnum.VOD_FILE_ID_ERROR);
            }
            //	将最新流媒体对应的字段数据，要赋值给要修改的声音对象
            trackInfo.setMediaUrl(trackMediaInfo.getMediaUrl());
            trackInfo.setMediaDuration(trackMediaInfo.getDuration());
            trackInfo.setMediaType(trackMediaInfo.getType());
            trackInfo.setMediaSize(trackMediaInfo.getSize());
            //	要删除原有云点播中的声音文件
            //			vodService.removeMediaInfo(mediaFileId);
            //			//	删除声音对应的统计表的数据
            //			LambdaQueryWrapper<TrackStat> wrapper = new LambdaQueryWrapper<>();
            //			wrapper.eq(TrackStat::getTrackId,trackId);
            //			this.trackStatMapper.delete(wrapper);

            //	给对应的声音Id 初始化统计
            //	声音统计 track_stat   trackId != media_file_id
            //			this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PLAY);
            //			this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COLLECT);
            //			this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PRAISE);
            //			this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COMMENT);
        }
        //	执行update 方法.
        this.updateById(trackInfo);
    }

    @Override
    public TrackInfo getTrackInfo(Long trackId) {
        //	返回数据
        //	1. 2. 3. 4
        return this.getById(trackId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeTrackInfo(Long trackId) {
        //	先根据声音Id 获取到声音对象，再根据声音对象中的专辑Id获取到专辑对象
        TrackInfo trackInfo = this.getById(trackId);
        //	track_info，album_info，track_stat，media
        //	根据主键删除
        trackInfoMapper.deleteById(trackId);
        //	track_stat
        trackStatMapper.delete(new LambdaQueryWrapper<TrackStat>().eq(TrackStat::getTrackId, trackId));
        //	update album_info  set include_track_count = include_track_count-1 where id = ?;

        AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
        //	开始更新
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() - 1);
        albumInfoMapper.updateById(albumInfo);
        //	删除流媒体： 3270835012672935433
        vodService.removeMediaInfo(trackInfo.getMediaFileId());
    }

    @Override
    public IPage<TrackListVo> findUserTrackPage(Page<TrackListVo> trackInfoPage, TrackInfoQuery trackInfoQuery) {
        //	调用mapper 层方法
        return trackInfoMapper.selectUserTrackPage(trackInfoPage, trackInfoQuery);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveTrackInfo(TrackInfoVo trackInfoVo, Long userId) {
		/*
		album_info update
		track_info insert
		track_stat insert
		 */
        //	创建对象
        TrackInfo trackInfo = new TrackInfo();
        //	属性拷贝
        BeanUtils.copyProperties(trackInfoVo, trackInfo);
        //	赋值
        trackInfo.setUserId(userId);
        //	order_num :  声音在专辑中的排序值，从1开始依次递增，值越小排序越前，
        //	根据专辑Id查询上一条声音的排序值 并且按照声音Id 进行降序排列 并且 获取第一条数
        //	select order_num from track_info where album_id = 1 order by order_num desc limit 1;
        LambdaQueryWrapper<TrackInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TrackInfo::getAlbumId, trackInfoVo.getAlbumId());
        wrapper.select(TrackInfo::getOrderNum);
        wrapper.last(" limit 1 ");
        wrapper.orderByDesc(TrackInfo::getOrderNum);
        //	获取声音对象
        TrackInfo preTrackInfo = trackInfoMapper.selectOne(wrapper);
        //	声明一个orderNum 变量
        Integer orderNum = 1;
        //	判断
        if (null != preTrackInfo) {
            //	排序值累加
            orderNum = preTrackInfo.getOrderNum() + 1;
        }
        //	赋值排序值
        trackInfo.setOrderNum(orderNum);
        //	赋值 media_duration , media_size ,media_type, media_url
        TrackMediaInfoVo trackMediaInfoVo = vodService.getTrackMediaInfo(trackInfoVo.getMediaFileId());
        trackInfo.setMediaDuration(trackMediaInfoVo.getDuration());
        trackInfo.setMediaSize(trackMediaInfoVo.getSize());
        trackInfo.setMediaType(trackMediaInfoVo.getType());
        trackInfo.setMediaUrl(trackMediaInfoVo.getMediaUrl());

        //	保存数据
        trackInfoMapper.insert(trackInfo);

        //	update album_info set include_track_count = ? where id = ?;
        //	albumId = 1603 1=== include_track_count-1 1==include_track_count-2
        AlbumInfo albumInfo = this.albumInfoMapper.selectById(trackInfoVo.getAlbumId());
        //	赋值包含声音总数
        albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
        //	更新数据： ById 根据主键更新。 如果没有主键update();
        this.albumInfoMapper.updateById(albumInfo);

        //	声音统计 track_stat
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PLAY);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COLLECT);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PRAISE);
        this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COMMENT);

    }

    private void saveTrackStat(Long trackId, String statType) {
        //	创建声音统计对象
        TrackStat trackStat = new TrackStat();
        //	赋值：
        trackStat.setTrackId(trackId);
        trackStat.setStatType(statType);
        trackStat.setStatNum(0);
        //	保存数据
        trackStatMapper.insert(trackStat);
    }

    @Override
    public Map<String, Object> uploadTrack(MultipartFile file) {
        //	初始化客户端
        VodUploadClient client = new VodUploadClient(vodConstantProperties.getSecretId(), vodConstantProperties.getSecretKey());
        //	设置请求对象
        VodUploadRequest request = new VodUploadRequest();
        String tempPath = UploadFileUtil.uploadTempPath(vodConstantProperties.getTempPath(), file);
        //	设置路径
        request.setMediaFilePath(tempPath);
        //	设置封面
        //	request.setCoverFilePath("/data/videos/Wildlife.jpg");
        //	指定任务流：
        request.setProcedure(vodConstantProperties.getProcedure());
        //	指定应用Id Long
        //	request.setSubAppId(vodConstantProperties.getAppId());

        try {
            //	上传之后获取到返回对象
            VodUploadResponse response = client.upload(vodConstantProperties.getRegion(), request);

            //	创建Map 集合
            Map<String, Object> map = new HashMap<>();
            map.put("mediaFileId", response.getFileId());
            map.put("mediaUrl", response.getMediaUrl());
            log.info("Upload FileId = {}", response.getFileId());
            //	返回数据
            return map;
        } catch (Exception e) {
            // 业务方进行异常处理
            log.error("Upload Err", e);
        }
        //	默认返回空
        return null;
    }
}
